/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.satish.gmailapp.service;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.batch.BatchRequest;
import com.google.api.client.googleapis.batch.json.JsonBatchCallback;
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.model.ListMessagesResponse;
import com.google.api.services.gmail.model.ListThreadsResponse;
import com.google.api.services.gmail.model.Message;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.google.api.services.gmail.model.Thread;
import com.satish.gmailapp.beans.SearchCriteria;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.logging.log4j.LogManager;

/**
 *
 * @author satish
 */
public class GmailService {
    private static final org.apache.logging.log4j.Logger logger = LogManager.getLogger(GmailService.class);

    private String clientId;

    private String clientSecret;

    private static final List<String> SCOPE = Arrays.asList(
            ("https://www.googleapis.com/auth/userinfo.profile;" +
                    "https://www.googleapis.com/auth/userinfo.email;" +
                    "https://www.googleapis.com/auth/gmail.modify;" +
                    "https://mail.google.com").split(";"));
    private static final JsonFactory JSON_FACTORY = new JacksonFactory();
    private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();

    private GoogleAuthorizationCodeFlow flow;

    public GmailService() {
        // Configure logging for the google api libraries
        Logger httpLogger = Logger.getLogger("com.google.api.client");
        httpLogger.setLevel(Level.ALL);
        ConsoleHandler logHandler = new ConsoleHandler();
        logHandler.setLevel(Level.ALL);
        httpLogger.addHandler(logHandler);
    }
    

    public List<Thread> getThreads(Credential credential) throws IOException {
        Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).build();
        ListThreadsResponse threadsResponse = service.users().threads().list("me").execute();
        return threadsResponse.getThreads();
    }
    
    public List<Message> getMessages(Credential credential) throws IOException {
        Gmail gmail = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName("Doh").build();
        ListMessagesResponse msgsResponse = gmail.users().messages().list("me").execute();
        List<Message> msgs = msgsResponse.getMessages();
        
        // Now the message returned will have only the id and thread id. We need to get the full message.
        // So we will use Messages.get() to do it. And we will batchRequest the get requests for better performance.
        BatchRequest batchRequest = gmail.batch();
        final List<Message> messageList = new ArrayList<>(100); // We know Google will return a max of 100 messages for Messages.list()
        JsonBatchCallback<Message> jsonBatchCallback = new JsonBatchCallback<Message>() {

            @Override
            public void onFailure(GoogleJsonError gje, HttpHeaders hh) throws IOException {
                logger.error(gje.getMessage());
            }

            @Override
            public void onSuccess(Message t, HttpHeaders hh) throws IOException {
                messageList.add(t);
            }
        };
        for(Message message : msgs) {
            gmail.users().messages().get("me", message.getId()).queue(batchRequest, jsonBatchCallback);
        }
        batchRequest.execute();
        
        return messageList;
    }
    
    public Message getMessage(String id, Credential credential) throws IOException {
        Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).build();
        return service.users().messages().get("me", id).execute();
    }
    
    /**
     *
     * @param criteria
     * @param credential
     * @return
     * @throws IOException
     */
    public int trashMessages(SearchCriteria criteria, Credential credential) throws IOException {
        Gmail service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).build();
        ListMessagesResponse msgsResponse = service.users().messages().list("me").setQ(criteria.toString()).execute();
        List<Message> messageList = new ArrayList<>();

        // Get all messageList that match our search criteria
        while (msgsResponse.getMessages() != null) {
            messageList.addAll(msgsResponse.getMessages());
            if (msgsResponse.getNextPageToken() != null) {
                String pageToken = msgsResponse.getNextPageToken();
                msgsResponse = service.users().messages().list("me").setQ(criteria.toString())
                        .setPageToken(pageToken).execute();
            } else {
                break;
            }
        }
        
        BatchRequest batchRequest = service.batch();
        JsonBatchCallback<Message> jsonBatchCallback = new JsonBatchCallback<Message>() {

            @Override
            public void onFailure(GoogleJsonError gje, HttpHeaders hh) throws IOException {
                logger.error(gje.getMessage());
            }

            @Override
            public void onSuccess(Message t, HttpHeaders hh) throws IOException {

            }
        };

        // TODO: For some reason Google has a problem with trashing messages as a batch.
        // TODO: Need to find a workaround. For now, lets delete messgaes one by one.
//        for(int i=1; i<=messageList.size(); i++) {
//            Message message = messageList.get(i -1);
//            service.users().messages().trash("me", message.getId()).queue(batchRequest, jsonBatchCallback);
//            if(i % 100 == 0) {
//                logger.debug("100 boundary: " + (i -1));
//                batchRequest.execute();
//            }
//        }
        
        for(Message message : messageList) {
            service.users().messages().trash("me", message.getId()).execute();
        }
        
        return messageList.size();
    }
    
    public GoogleAuthorizationCodeFlow getGoogleAuthorizationCodeFlow() {
        if(flow == null) {
            flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT,
                    JSON_FACTORY, clientId, clientSecret, SCOPE).setAccessType("offline").build();
        }
        return flow;
    }

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public String getClientSecret() {
        return clientSecret;
    }

    public void setClientSecret(String clientSecret) {
        this.clientSecret = clientSecret;
    }
}